//
// Created by pulsarv on 19-12-8.
//

#include <list>
#include <memory>
#include <set>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <opencv2/opencv.hpp>
#include <input_wrappers.h>

namespace MobileSearch {
    std::shared_ptr<InputChannel> InputChannel::create(const std::shared_ptr<IInputSource> &source) {
        auto tmp = std::shared_ptr<InputChannel>(new InputChannel(source));
        source->addSubscriber(tmp);
        return tmp;
    }

    bool InputChannel::read(cv::Mat &mat) {
        readQueueMutex.lock();
        if (readQueue.empty()) {
            readQueueMutex.unlock();
            source->lock();
            readQueueMutex.lock();
            if (readQueue.empty()) {
                bool res = source->read(mat, shared_from_this());
                readQueueMutex.unlock();
                source->unlock();
                return res;
            } else {
                source->unlock();
            }
        }
        mat = readQueue.front().clone();
        readQueue.pop();
        readQueueMutex.unlock();
        return true;
    }

    void InputChannel::push(const cv::Mat &mat) {
        readQueueMutex.lock();
        readQueue.push(mat);
        readQueueMutex.unlock();
    }

    cv::Size InputChannel::getSize() {
        return source->getSize();
    }


    VideoCaptureSource::VideoCaptureSource(const cv::VideoCapture &videoCapture, bool loop) : videoCapture{
            videoCapture}, loop{loop},
                                                                                              imSize{static_cast<int>(videoCapture.get(
                                                                                                      cv::CAP_PROP_FRAME_WIDTH)),
                                                                                                     static_cast<int>(videoCapture.get(
                                                                                                             cv::CAP_PROP_FRAME_HEIGHT))} {

    }

    bool VideoCaptureSource::read(cv::Mat &mat, const std::shared_ptr<InputChannel> &caller) {
        if (!videoCapture.read(mat)) {
            if (loop) {
                videoCapture.set(cv::CAP_PROP_POS_FRAMES, 0);
                videoCapture.read(mat);
            } else {
                return false;
            }
        }
        if (1 != subscribedInputChannels.size()) {
            cv::Mat shared = mat.clone();
            for (const std::weak_ptr<InputChannel> &weakInputChannel : subscribedInputChannels) {
                try {
                    std::shared_ptr<InputChannel> sharedInputChannel = std::shared_ptr<InputChannel>(
                            weakInputChannel);
                    if (caller != sharedInputChannel) {
                        sharedInputChannel->push(shared);
                    }
                } catch (const std::bad_weak_ptr &) {}
            }
        }
        return true;
    }

    void VideoCaptureSource::addSubscriber(const std::weak_ptr<InputChannel> &inputChannel) {
        subscribedInputChannels.push_back(inputChannel);
    }

    cv::Size VideoCaptureSource::getSize() {
        return imSize;
    }

}




